Read counts data

Load and transpose counts file (Sample IDs as header and miRNA IDs as first column)

oriCountsFile <- read.csv(file = './mature_counts.csv')
countsFile <- as.data.frame(t(oriCountsFile)) # transpose oriCountsFile
countsFile <- janitor::row_to_names(countsFile, row_number=1)
countsFile <- tibble::rownames_to_column(countsFile, var="miR")
countsFile$miR <- chartr('.', '-', countsFile$miR) # replace '.' with '-' in miR
countsFile

Update the IDs

update <- miRNAVersionConvert(countsFile$miR)
head(update)
length(unique(countsFile$miR))
length(unique(update[,"OriginalName"]))
length(unique(update[,"TargetName"]))

Fetch the mirbase22 IDs and sequences

mrbse <- read.table("../../miRNA/mirbase/mature_homo-sapiens_dataframe.txt", header = T, sep = "\t")
mrbse <- mrbse[-c(1,2),] # The first two rows don't match up with mirbase, are the only duplicates, and are the only sequences with T's for some reason. I'll remove them.
head(mrbse)

Create microRNA Mapping table and Update Countfile miRNA Row Names

mrbse2 <- merge(mrbse, update, by.x = 1, by.y = 2, all.x = F, all.y = T)
mrbse2 <- mrbse2[,-4]
dim(mrbse2)
[1] 2423    3
head(mrbse2)
rownames(mrbse2)<- mrbse2[,"OriginalName"]
rownames(countsFile)<-countsFile[,1]
rownames(countsFile)<- mrbse2[rownames(countsFile), "Name"]

Create Row for each miRNA and Merge this with the data

mergedData <- matrix(0, nrow = dim(mrbse)[1], ncol= 302)
mergedData <- data.frame(mergedData)
mergedData<- cbind(mrbse, mergedData)
colnames(mergedData)<- c("Name", "Seq", colnames(countsFile)[-1])
rownames(mergedData)<-mergedData[,1]
mergedData[rownames(countsFile), colnames(countsFile)[-1]]<- countsFile[,-1]
#mergedData <- merge(mrbse2, countsFile, by.x = 3, by.y = 1, all.x = T, all.y = F)
dim(mergedData)
[1] 2656  304
head(mergedData)
#mergedData <- mergedData[,-1] # Remove the original (old) IDs
mergedData[1:5,1:5]
mergedData

Quintize the data

For each column: assign each miRNA a value 1-5 depending on which of the 20th percentiles it falls into. If the value is 0, it remains a 0

# Given a column, assign a number to each element from 0-5. All
# 0s get a 0, and the rest get a value according to the 20th percentile
# that it falls in among the non-zero values.
quintize <- function(vec) {
  qntls <- c(0, quantile(vec[which(vec != 0)], 0.2*(1:4)))
  vec2 <- sapply(vec, function(x) {
    if (x == 0) return(0)
    else return(max(which(x > qntls)))
  })
  return(vec2)
}
mergedData[,3:ncol(mergedData)] = lapply(mergedData[,3:ncol(mergedData)], FUN = function(y){as.numeric(y)})
quintizedData <- apply(mergedData[,-c(1:2)], 2, quintize)
quintizedData <- cbind(mergedData[,1:2], quintizedData)
quintizedData[1:5,1:5]

Combine the replicates

Get the meta information

meta <- read.table("./meta.txt", header = T, sep = "\t")
meta$Sample_ID <- gsub("BioSample: https://www.ncbi.nlm.nih.gov/biosample/", "", meta$X.Sample_relation) # extract only SAMN IDs from X.Sample_relation

Create Disease, Tissue, and Group (Tissue_Disease) columns

meta$Tissue <- str_replace(meta$X.Sample_source_name_ch1, "tissue: ", "")
meta$Tissue <- str_replace_all(meta$Tissue, " ", ".")
meta$Tissue <- tolower(meta$Tissue)
meta$Disease <- str_replace_all(meta$X.Sample_characteristics_ch1, "disease status: ", "")
meta$Disease <- str_replace_all(meta$Disease, " ", ".")
meta$Group <- apply(meta[,c(ncol(meta)-1,ncol(meta))], 1, function(x) paste(x[1], x[2], sep="_"))
meta$Group <- str_replace_all(meta$Group, "_Adjacent.Normal", "")
meta$Disease <- str_replace_all(meta$Disease, "Adjacent Normal", "")

Grouping: Combine replicates (samples that have the same type)

uniqueTissues <- unique(meta$Group)
meta$Sample_ID %in% names(quintizedData)
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[28] TRUE TRUE TRUE TRUE TRUE
combinedData <- lapply(uniqueTissues, function(x) {
  df <- as.data.frame(quintizedData[,meta$Sample_ID[which(meta$Group == x)]])
  return(rowMeans(df))
})
names(combinedData) <- uniqueTissues
combinedData <- do.call(cbind, combinedData)
combinedData <- cbind(quintizedData[,1:2], combinedData)

Ensure the terms are standardized

Ensure that all the group names appear in the ontology or the corrections file.

Get the ontology and correction files

ont <- read.table("../ontology.txt", header = F, sep = "\t")
corr <- read.table("../corrections.txt", header = T, sep = "\t")

Aim to have 0 as a result, meaning all terms are either already in the ontology or the correction file. Or else: update correction file or ontology file, or both.

trms <- unique(unlist(strsplit(names(combinedData)[3:ncol(combinedData)], "_"))) # Splits the composite terms that contain both tissue+disease
if (length(which(trms %in% union(corr[,1], unique(unlist(ont[,c(1,3)]))))) != 0){
  trms[-which(trms %in% union(corr[,1], unique(unlist(ont[,c(1,3)]))))] # Which terms aren't in the ontology or the corrected terms
} else {
  trms
}
character(0)

Found 2 new terms: HCC, Adjacent.Normal. Terms are updated in ontology file and correction file, we just need to update them with the corrected terms.

colnames(combinedData) <- sapply(colnames(combinedData), function(z) {
  y <- strsplit(z, "_")[[1]]
  retVal <- sapply(y, function(x) {
    if (x %in% corr$currentTerm) {
      return(corr$correctedTerm[match(x, corr$currentTerm)])
    } else {
      return(x)
    }
  })
  return(paste(retVal, collapse = "_"))
})

Convert to long format

Add the Canonical column and Source column

data <- combinedData # all tissues have to exist in ontology or correction files
data$Canonical <- sapply(data$Seq, function(x) if (x %in% mrbse$Seq) return(T) else return(F))
data$Source <- "Varghese*"
data <- data[,c(1,2,ncol(data)-1,ncol(data),3:(ncol(data)-2))] # Set columns alignment
head(data)

Convert to long format

data_long <- melt(data, id.vars=c("Name", "Seq", "Canonical", "Source"))

Binarization

data_long$Binary <- sapply(data_long$value, function(x) if (x == 0) return(0) else return(1))
names(data_long) <- c("miR", "Seq", "Canonical", "Source", "Tissue", "Scale", "Binary")

Write to file

write.table(data_long, paste(sep="", result_path,"/varghese_longData.txt"), sep = "\t", row.names = F, col.names = T, quote = F)
write.table(unique(data_long[,1:3]), paste(sep="", result_path,"/varghese_miRNAs.txt"), sep = "\t", row.names = F, col.names = T, quote = F)
writeLines(as.character(unique(data_long$Tissue)), paste(sep="", result_path,"/varghese_tissues.txt"))
LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBWYXJnaGVzZSBleHByZXNzaW9uIGRhdGEiCmF1dGhvcjogIkdpdHRhIEVrYXB1dGVyaSwgQW5uZS1DaHJpc3RpbiBIYXVzY2hpbGQiCmRhdGU6ICIyOS8wOC8yMDIyIiAKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShtaVJCYXNlQ29udmVydGVyKQpsaWJyYXJ5KHJlc2hhcGUyKQpyb290X3BhdGggPC0gIi4vIgpyZXN1bHRfcGF0aCA8LSBwYXN0ZShyb290X3BhdGgsICIvZGF0YS9taVJOQV9maW5hbCIsIHNlcD0iIikKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBwYXN0ZShzZXA9IiIsIHJvb3RfcGF0aCwgImRhdGEvbWlSTkEvdmFyZ2hlc2VEYXRhLyIpKQpzZXR3ZChwYXN0ZShzZXA9IiIsIHJvb3RfcGF0aCwgImRhdGEvbWlSTkEvdmFyZ2hlc2VEYXRhLyIpKQpgYGAKCiMjIyBSZWFkIGNvdW50cyBkYXRhCgpMb2FkIGFuZCB0cmFuc3Bvc2UgY291bnRzIGZpbGUgKFNhbXBsZSBJRHMgYXMgaGVhZGVyIGFuZCBtaVJOQSBJRHMgYXMgZmlyc3QgY29sdW1uKQpgYGB7cn0Kb3JpQ291bnRzRmlsZSA8LSByZWFkLmNzdihmaWxlID0gJy4vbWF0dXJlX2NvdW50cy5jc3YnKQpjb3VudHNGaWxlIDwtIGFzLmRhdGEuZnJhbWUodChvcmlDb3VudHNGaWxlKSkgIyB0cmFuc3Bvc2Ugb3JpQ291bnRzRmlsZQpjb3VudHNGaWxlIDwtIGphbml0b3I6OnJvd190b19uYW1lcyhjb3VudHNGaWxlLCByb3dfbnVtYmVyPTEpCmNvdW50c0ZpbGUgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oY291bnRzRmlsZSwgdmFyPSJtaVIiKQpjb3VudHNGaWxlJG1pUiA8LSBjaGFydHIoJy4nLCAnLScsIGNvdW50c0ZpbGUkbWlSKSAjIHJlcGxhY2UgJy4nIHdpdGggJy0nIGluIG1pUgpjb3VudHNGaWxlCmBgYAoKIyMjIFVwZGF0ZSB0aGUgSURzIAoKYGBge3J9CnVwZGF0ZSA8LSBtaVJOQVZlcnNpb25Db252ZXJ0KGNvdW50c0ZpbGUkbWlSKQpoZWFkKHVwZGF0ZSkKbGVuZ3RoKHVuaXF1ZShjb3VudHNGaWxlJG1pUikpCmxlbmd0aCh1bmlxdWUodXBkYXRlWywiT3JpZ2luYWxOYW1lIl0pKQpsZW5ndGgodW5pcXVlKHVwZGF0ZVssIlRhcmdldE5hbWUiXSkpCgpgYGAKCkZldGNoIHRoZSBtaXJiYXNlMjIgSURzIGFuZCBzZXF1ZW5jZXMKCmBgYHtyfQptcmJzZSA8LSByZWFkLnRhYmxlKCIuLi8uLi9taVJOQS9taXJiYXNlL21hdHVyZV9ob21vLXNhcGllbnNfZGF0YWZyYW1lLnR4dCIsIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIpCm1yYnNlIDwtIG1yYnNlWy1jKDEsMiksXSAjIFJlbW92ZSB0aGUgZmlyc3QgdHdvIHJvd3MgdGhhdCBkb24ndCBtYXRjaCB1cCB3aXRoIG1pcmJhc2UgYW5kIGFyZSB0aGUgb25seSBkdXBsaWNhdGVzLCBhbmQgYXJlIHRoZSBvbmx5IHNlcXVlbmNlcyB3aXRoIFQncyBmb3Igc29tZSByZWFzb24uCmhlYWQobXJic2UpCmBgYAoKQ3JlYXRlIG1pY3JvUk5BIE1hcHBpbmcgdGFibGUgYW5kIFVwZGF0ZSBDb3VudGZpbGUgbWlSTkEgUm93IE5hbWVzCmBgYHtyfQptcmJzZTIgPC0gbWVyZ2UobXJic2UsIHVwZGF0ZSwgYnkueCA9IDEsIGJ5LnkgPSAyLCBhbGwueCA9IEYsIGFsbC55ID0gVCkKbXJic2UyIDwtIG1yYnNlMlssLTRdCmRpbShtcmJzZTIpCmhlYWQobXJic2UyKQpyb3duYW1lcyhtcmJzZTIpPC0gbXJic2UyWywiT3JpZ2luYWxOYW1lIl0Kcm93bmFtZXMoY291bnRzRmlsZSk8LWNvdW50c0ZpbGVbLDFdCnJvd25hbWVzKGNvdW50c0ZpbGUpPC0gbXJic2UyW3Jvd25hbWVzKGNvdW50c0ZpbGUpLCAiTmFtZSJdCmBgYAoKCkNyZWF0ZSBSb3cgZm9yIGVhY2ggbWlSTkEgYW5kIE1lcmdlIHRoaXMgd2l0aCB0aGUgZGF0YQoKYGBge3J9Cm1lcmdlZERhdGEgPC0gbWF0cml4KDAsIG5yb3cgPSBkaW0obXJic2UpWzFdLCBuY29sPSAzMDIpCm1lcmdlZERhdGEgPC0gZGF0YS5mcmFtZShtZXJnZWREYXRhKQptZXJnZWREYXRhPC0gY2JpbmQobXJic2UsIG1lcmdlZERhdGEpCmNvbG5hbWVzKG1lcmdlZERhdGEpPC0gYygiTmFtZSIsICJTZXEiLCBjb2xuYW1lcyhjb3VudHNGaWxlKVstMV0pCnJvd25hbWVzKG1lcmdlZERhdGEpPC1tZXJnZWREYXRhWywxXQptZXJnZWREYXRhW3Jvd25hbWVzKGNvdW50c0ZpbGUpLCBjb2xuYW1lcyhjb3VudHNGaWxlKVstMV1dPC0gY291bnRzRmlsZVssLTFdCiNtZXJnZWREYXRhIDwtIG1lcmdlKG1yYnNlMiwgY291bnRzRmlsZSwgYnkueCA9IDMsIGJ5LnkgPSAxLCBhbGwueCA9IFQsIGFsbC55ID0gRikKZGltKG1lcmdlZERhdGEpCmhlYWQobWVyZ2VkRGF0YSkKI21lcmdlZERhdGEgPC0gbWVyZ2VkRGF0YVssLTFdICMgUmVtb3ZlIHRoZSBvcmlnaW5hbCAob2xkKSBJRHMKbWVyZ2VkRGF0YVsxOjUsMTo1XQptZXJnZWREYXRhCmBgYAoKCgoKIyMjIFF1aW50aXplIHRoZSBkYXRhCgpGb3IgZWFjaCBjb2x1bW46IGFzc2lnbiBlYWNoIG1pUk5BIGEgdmFsdWUgMS01IGRlcGVuZGluZyBvbiB3aGljaCBvZiB0aGUgMjB0aCBwZXJjZW50aWxlcyBpdCBmYWxscyBpbnRvLiBJZiB0aGUgdmFsdWUgaXMgMCwgaXQgcmVtYWlucyBhIDAKCmBgYHtyfQojIEdpdmVuIGEgY29sdW1uLCBhc3NpZ24gYSBudW1iZXIgdG8gZWFjaCBlbGVtZW50IGZyb20gMC01LiBBbGwKIyAwcyBnZXQgYSAwLCBhbmQgdGhlIHJlc3QgZ2V0IGEgdmFsdWUgYWNjb3JkaW5nIHRvIHRoZSAyMHRoIHBlcmNlbnRpbGUKIyB0aGF0IGl0IGZhbGxzIGluIGFtb25nIHRoZSBub24temVybyB2YWx1ZXMuCnF1aW50aXplIDwtIGZ1bmN0aW9uKHZlYykgewogIHFudGxzIDwtIGMoMCwgcXVhbnRpbGUodmVjW3doaWNoKHZlYyAhPSAwKV0sIDAuMiooMTo0KSkpCiAgdmVjMiA8LSBzYXBwbHkodmVjLCBmdW5jdGlvbih4KSB7CiAgICBpZiAoeCA9PSAwKSByZXR1cm4oMCkKICAgIGVsc2UgcmV0dXJuKG1heCh3aGljaCh4ID4gcW50bHMpKSkKICB9KQogIHJldHVybih2ZWMyKQp9CmBgYAoKYGBge3J9Cm1lcmdlZERhdGFbLDM6bmNvbChtZXJnZWREYXRhKV0gPSBsYXBwbHkobWVyZ2VkRGF0YVssMzpuY29sKG1lcmdlZERhdGEpXSwgRlVOID0gZnVuY3Rpb24oeSl7YXMubnVtZXJpYyh5KX0pCnF1aW50aXplZERhdGEgPC0gYXBwbHkobWVyZ2VkRGF0YVssLWMoMToyKV0sIDIsIHF1aW50aXplKQpxdWludGl6ZWREYXRhIDwtIGNiaW5kKG1lcmdlZERhdGFbLDE6Ml0sIHF1aW50aXplZERhdGEpCnF1aW50aXplZERhdGFbMTo1LDE6NV0KYGBgCgoKCgojIyMgQ29tYmluZSB0aGUgcmVwbGljYXRlcwoKR2V0IHRoZSBtZXRhIGluZm9ybWF0aW9uCgpgYGB7cn0KbWV0YSA8LSByZWFkLnRhYmxlKCIuL21ldGEudHh0IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKbWV0YSRTYW1wbGVfSUQgPC0gZ3N1YigiQmlvU2FtcGxlOiBodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3NhbXBsZS8iLCAiIiwgbWV0YSRYLlNhbXBsZV9yZWxhdGlvbikgIyBleHRyYWN0IG9ubHkgU0FNTiBJRHMgZnJvbSBYLlNhbXBsZV9yZWxhdGlvbgpgYGAKCgpDcmVhdGUgRGlzZWFzZSwgVGlzc3VlLCBhbmQgR3JvdXAgKFRpc3N1ZV9EaXNlYXNlKSBjb2x1bW5zCgpgYGB7cn0KbWV0YSRUaXNzdWUgPC0gc3RyX3JlcGxhY2UobWV0YSRYLlNhbXBsZV9zb3VyY2VfbmFtZV9jaDEsICJ0aXNzdWU6ICIsICIiKQptZXRhJFRpc3N1ZSA8LSBzdHJfcmVwbGFjZV9hbGwobWV0YSRUaXNzdWUsICIgIiwgIi4iKQptZXRhJFRpc3N1ZSA8LSB0b2xvd2VyKG1ldGEkVGlzc3VlKQptZXRhJERpc2Vhc2UgPC0gc3RyX3JlcGxhY2VfYWxsKG1ldGEkWC5TYW1wbGVfY2hhcmFjdGVyaXN0aWNzX2NoMSwgImRpc2Vhc2Ugc3RhdHVzOiAiLCAiIikKbWV0YSREaXNlYXNlIDwtIHN0cl9yZXBsYWNlX2FsbChtZXRhJERpc2Vhc2UsICIgIiwgIi4iKQptZXRhJEdyb3VwIDwtIGFwcGx5KG1ldGFbLGMobmNvbChtZXRhKS0xLG5jb2wobWV0YSkpXSwgMSwgZnVuY3Rpb24oeCkgcGFzdGUoeFsxXSwgeFsyXSwgc2VwPSJfIikpCm1ldGEkR3JvdXAgPC0gc3RyX3JlcGxhY2VfYWxsKG1ldGEkR3JvdXAsICJfQWRqYWNlbnQuTm9ybWFsIiwgIiIpCm1ldGEkRGlzZWFzZSA8LSBzdHJfcmVwbGFjZV9hbGwobWV0YSREaXNlYXNlLCAiQWRqYWNlbnQgTm9ybWFsIiwgIiIpCmBgYAoKCkdyb3VwaW5nOiBDb21iaW5lIHJlcGxpY2F0ZXMgKHNhbXBsZXMgdGhhdCBoYXZlIHRoZSBzYW1lIHR5cGUpCgpgYGB7cn0KdW5pcXVlVGlzc3VlcyA8LSB1bmlxdWUobWV0YSRHcm91cCkKbWV0YSRTYW1wbGVfSUQgJWluJSBuYW1lcyhxdWludGl6ZWREYXRhKQpjb21iaW5lZERhdGEgPC0gbGFwcGx5KHVuaXF1ZVRpc3N1ZXMsIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHF1aW50aXplZERhdGFbLG1ldGEkU2FtcGxlX0lEW3doaWNoKG1ldGEkR3JvdXAgPT0geCldXSkKICByZXR1cm4ocm93TWVhbnMoZGYpKQp9KQpuYW1lcyhjb21iaW5lZERhdGEpIDwtIHVuaXF1ZVRpc3N1ZXMKY29tYmluZWREYXRhIDwtIGRvLmNhbGwoY2JpbmQsIGNvbWJpbmVkRGF0YSkKY29tYmluZWREYXRhIDwtIGNiaW5kKHF1aW50aXplZERhdGFbLDE6Ml0sIGNvbWJpbmVkRGF0YSkKYGBgCgoKIyMjIEVuc3VyZSB0aGUgdGVybXMgYXJlIHN0YW5kYXJkaXplZApFbnN1cmUgdGhhdCBhbGwgdGhlIGdyb3VwIG5hbWVzIGFwcGVhciBpbiB0aGUgb250b2xvZ3kgb3IgdGhlIGNvcnJlY3Rpb25zIGZpbGUuCgpHZXQgdGhlIG9udG9sb2d5IGFuZCBjb3JyZWN0aW9uIGZpbGVzCgpgYGB7cn0Kb250IDwtIHJlYWQudGFibGUoIi4uL29udG9sb2d5LnR4dCIsIGhlYWRlciA9IEYsIHNlcCA9ICJcdCIpCmNvcnIgPC0gcmVhZC50YWJsZSgiLi4vY29ycmVjdGlvbnMudHh0IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKYGBgCgpBaW0gdG8gaGF2ZSAwIGFzIGEgcmVzdWx0LCBtZWFuaW5nIGFsbCB0ZXJtcyBhcmUgZWl0aGVyIGFscmVhZHkgaW4gdGhlIG9udG9sb2d5IG9yIHRoZSBjb3JyZWN0aW9uIGZpbGUuIE9yIGVsc2U6IHVwZGF0ZSBjb3JyZWN0aW9uIGZpbGUgb3Igb250b2xvZ3kgZmlsZSwgb3IgYm90aC4KCmBgYHtyfQp0cm1zIDwtIHVuaXF1ZSh1bmxpc3Qoc3Ryc3BsaXQobmFtZXMoY29tYmluZWREYXRhKVszOm5jb2woY29tYmluZWREYXRhKV0sICJfIikpKSAjIFNwbGl0cyB0aGUgY29tcG9zaXRlIHRlcm1zIHRoYXQgY29udGFpbiBib3RoIHRpc3N1ZStkaXNlYXNlCmlmIChsZW5ndGgod2hpY2godHJtcyAlaW4lIHVuaW9uKGNvcnJbLDFdLCB1bmlxdWUodW5saXN0KG9udFssYygxLDMpXSkpKSkpICE9IDApewogIHRybXNbLXdoaWNoKHRybXMgJWluJSB1bmlvbihjb3JyWywxXSwgdW5pcXVlKHVubGlzdChvbnRbLGMoMSwzKV0pKSkpXSAjIFdoaWNoIHRlcm1zIGFyZW4ndCBpbiB0aGUgb250b2xvZ3kgb3IgdGhlIGNvcnJlY3RlZCB0ZXJtcwp9IGVsc2UgewogIHRybXMKfQpgYGAKRm91bmQgMiBuZXcgdGVybXM6IEhDQywgQWRqYWNlbnQuTm9ybWFsLiBUZXJtcyBhcmUgdXBkYXRlZCBpbiBvbnRvbG9neSBmaWxlIGFuZCBjb3JyZWN0aW9uIGZpbGUsIHdlIGp1c3QgbmVlZCB0byB1cGRhdGUgdGhlbSB3aXRoIHRoZSBjb3JyZWN0ZWQgdGVybXMuCgpgYGB7cn0KY29sbmFtZXMoY29tYmluZWREYXRhKSA8LSBzYXBwbHkoY29sbmFtZXMoY29tYmluZWREYXRhKSwgZnVuY3Rpb24oeikgewogIHkgPC0gc3Ryc3BsaXQoeiwgIl8iKVtbMV1dCiAgcmV0VmFsIDwtIHNhcHBseSh5LCBmdW5jdGlvbih4KSB7CiAgICBpZiAoeCAlaW4lIGNvcnIkY3VycmVudFRlcm0pIHsKICAgICAgcmV0dXJuKGNvcnIkY29ycmVjdGVkVGVybVttYXRjaCh4LCBjb3JyJGN1cnJlbnRUZXJtKV0pCiAgICB9IGVsc2UgewogICAgICByZXR1cm4oeCkKICAgIH0KICB9KQogIHJldHVybihwYXN0ZShyZXRWYWwsIGNvbGxhcHNlID0gIl8iKSkKfSkKYGBgCgojIyMgQ29udmVydCB0byBsb25nIGZvcm1hdApBZGQgdGhlIENhbm9uaWNhbCBjb2x1bW4gYW5kIFNvdXJjZSBjb2x1bW4KCmBgYHtyfQpkYXRhIDwtIGNvbWJpbmVkRGF0YSAjIGFsbCB0aXNzdWVzIGhhdmUgdG8gZXhpc3QgaW4gb250b2xvZ3kgb3IgY29ycmVjdGlvbiBmaWxlcwpkYXRhJENhbm9uaWNhbCA8LSBzYXBwbHkoZGF0YSRTZXEsIGZ1bmN0aW9uKHgpIGlmICh4ICVpbiUgbXJic2UkU2VxKSByZXR1cm4oVCkgZWxzZSByZXR1cm4oRikpCmRhdGEkU291cmNlIDwtICJWYXJnaGVzZSoiCmRhdGEgPC0gZGF0YVssYygxLDIsbmNvbChkYXRhKS0xLG5jb2woZGF0YSksMzoobmNvbChkYXRhKS0yKSldICMgU2V0IGNvbHVtbnMgYWxpZ25tZW50CmhlYWQoZGF0YSkKYGBgCgoKQ29udmVydCB0byBsb25nIGZvcm1hdAoKYGBge3J9CmRhdGFfbG9uZyA8LSBtZWx0KGRhdGEsIGlkLnZhcnM9YygiTmFtZSIsICJTZXEiLCAiQ2Fub25pY2FsIiwgIlNvdXJjZSIpKQpgYGAKCkJpbmFyaXphdGlvbgpgYGB7cn0KZGF0YV9sb25nJEJpbmFyeSA8LSBzYXBwbHkoZGF0YV9sb25nJHZhbHVlLCBmdW5jdGlvbih4KSBpZiAoeCA9PSAwKSByZXR1cm4oMCkgZWxzZSByZXR1cm4oMSkpCm5hbWVzKGRhdGFfbG9uZykgPC0gYygibWlSIiwgIlNlcSIsICJDYW5vbmljYWwiLCAiU291cmNlIiwgIlRpc3N1ZSIsICJTY2FsZSIsICJCaW5hcnkiKQpgYGAKCgojIyMgV3JpdGUgdG8gZmlsZQoKYGBge3J9CndyaXRlLnRhYmxlKGRhdGFfbG9uZywgcGFzdGUoc2VwPSIiLCByZXN1bHRfcGF0aCwiL3ZhcmdoZXNlX2xvbmdEYXRhLnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCndyaXRlLnRhYmxlKHVuaXF1ZShkYXRhX2xvbmdbLDE6M10pLCBwYXN0ZShzZXA9IiIsIHJlc3VsdF9wYXRoLCIvdmFyZ2hlc2VfbWlSTkFzLnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKHVuaXF1ZShkYXRhX2xvbmckVGlzc3VlKSksIHBhc3RlKHNlcD0iIiwgcmVzdWx0X3BhdGgsIi92YXJnaGVzZV90aXNzdWVzLnR4dCIpKQpgYGAK